﻿using CustomCharacter.Ability;
using CustomCharacter.VoiceChat;
using Exiled.API.Enums;
using Exiled.API.Extensions;
using Exiled.API.Features;
using Exiled.API.Features.Pools;
using MEC;
using PlayerRoles;
using PlayerRoles.PlayableScps.Scp939;
using PlayerRoles.Spectating;
using PlayerStatsSystem;
using Respawning;
using Respawning.NamingRules;
using RgCore.GameData;
using RgCore.PlayerHud;
using Subtitles;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Text;
using CustomCharacter.Interfaces;
using RgCore.CustomSide;
using UnityEngine;
using static RgCore.API.API;
using RgCore.Menu;
using Text = RgCore.Menu.Text;
using RgCore.Modules;
using RgCore.RoleAssignment;

namespace CustomCharacter
{
    public static class MethodExtensions
    {
        public static string ColorToHex(this Color32 color)
        {
            return $"#{color.r:X2}{color.g:X2}{color.b:X2}";
        }

        public static CustomCharacter GetCharacterInstance(this Player player) => CustomCharacter.AllCharacters.FirstOrDefault(x => x.Id== player.Id);

        public static DamageHandlerBase.CassieAnnouncement GetCustomDeathAnnouncement(IScpNumber num, DamageHandlerBase handler)
        {
            string announce = "SCP";
            List<SubtitlePart> parts = new List<SubtitlePart> { new SubtitlePart(SubtitleType.SCP, num.ScpNumber.ToString()) };
            foreach (char n in num.ScpNumber.ToString())
            {
                announce += " " + n;
            }
            announce += " ";
            if (handler is UniversalDamageHandler udh)
            {
                if (DeathTranslations.PocketDecay.Id == udh.TranslationId)
                {
                    parts.Add(new SubtitlePart(SubtitleType.TerminatedBySCP, "106"));
                    announce += "TERMINATED BY POCKET DIMENSION";
                }
                else if (DeathTranslations.Decontamination.Id == udh.TranslationId)
                {
                    parts.Add(new SubtitlePart(SubtitleType.LostInDecontamination));
                    announce += "LOST IN DECONTAMINATION";
                }
                else if (DeathTranslations.Tesla.Id == udh.TranslationId)
                {
                    parts.Add(new SubtitlePart(SubtitleType.TerminatedBySecuritySystem));
                    announce += "SUCCESSFULLY TERMINATED BY AUTOMATIC SECURITY SYSTEM";
                }
                else
                {
                    parts.Add(new SubtitlePart(SubtitleType.TerminationCauseUnspecified));
                    announce += "SUCCESSFULLY TERMINATED . TERMINATION CAUSE UNSPECIFIED";
                }
            }
            else if (handler is AttackerDamageHandler adh)
            {
                Player attacker = Player.Get(adh.Attacker.Hub);
                if (adh is ScpDamageHandler || adh is Scp939DamageHandler || (attacker?.IsCustomCharacter() == true && attacker?.GetCharacterInstance() is IScpNumber))
                {
                    string number = adh is Scp939DamageHandler ? "939" : adh.Attacker.Role.ToString().Replace("Scp", "");
                    if (attacker?.GetCharacterInstance() is IScpNumber t)
                        number = t.ScpNumber;
                    parts.Add(new SubtitlePart(SubtitleType.TerminatedBySCP, number.Replace("0492", "049-2")));
                    announce += "TERMINATED BY SCP";
                    foreach (char n in number.Replace("-", ""))
                    {
                        announce += " " + n;
                    }
                }
                else
                {
                    switch (PlayerRolesUtils.GetTeam(adh.Attacker.Role))
                    {
                        case Team.ClassD:
                            parts.Add(new SubtitlePart(SubtitleType.ContainedByClassD));
                            announce += "CONAINEDSUCCESSFULLY BY CLASSD PERSONNEL";
                            break;

                        case Team.Scientists:
                            parts.Add(new SubtitlePart(SubtitleType.ContainedByScientist));
                            announce += "CONAINEDSUCCESSFULLY BY SCIENCE PERSONNEL";
                            break;

                        case Team.ChaosInsurgency:
                            parts.Add(new SubtitlePart(SubtitleType.ContainedByChaos));
                            announce += "CONAINEDSUCCESSFULLY BY CHAOSINSURGENCY";
                            break;

                        case Team.FoundationForces:
                            UnitNamingRule unitNamingRule;
                            if (!UnitNamingRule.TryGetNamingRule(SpawnableTeamType.NineTailedFox, out unitNamingRule))
                            {
                                announce += "CONTAINEDSUCCESSFULLY CONTAINMENTUNIT UNKNOWN";
                                parts.Add(new SubtitlePart(SubtitleType.ContainUnitUnknown));
                            }
                            else
                            {
                                string cassieUnitName = unitNamingRule.GetCassieUnitName(adh.Attacker.UnitName);
                                announce += "CONTAINEDSUCCESSFULLY CONTAINMENTUNIT " + cassieUnitName;
                                parts.Add(new SubtitlePart(SubtitleType.ContainUnit, adh.Attacker.UnitName));
                            }
                            break;

                        default:
                            announce += "SUCCESSFULLY TERMINATED . TERMINATION CAUSE UNSPECIFIED";
                            parts.Add(new SubtitlePart(SubtitleType.TerminationCauseUnspecified));
                            break;
                    }
                }
            }
            else if (handler is WarheadDamageHandler)
            {
                parts.Add(new SubtitlePart(SubtitleType.TerminatedByWarhead));
                announce += "TERMINATED BY ALPHA WARHEAD";
            }
            else
            {
                parts.Add(new SubtitlePart(SubtitleType.TerminationCauseUnspecified));
                announce += "SUCCESSFULLY TERMINATED . TERMINATION CAUSE UNSPECIFIED";
            }

            return new DamageHandlerBase.CassieAnnouncement() { Announcement = announce, SubtitleParts = parts.ToArray() };
        }

        public static string GetCustomDeathAnnouncement(string rolename, DamageHandlerBase handler)
        {
            string announce = $"[{rolename}]";
            announce += " ";
            if (handler is UniversalDamageHandler udh)
            {
                if (DeathTranslations.PocketDecay.Id == udh.TranslationId)
                {
                    announce += "遗失在了口袋空间。";
                }
                else if (DeathTranslations.Decontamination.Id == udh.TranslationId)
                {
                    announce += "已被轻收容净化系统清除。";
                }
                else if (DeathTranslations.Tesla.Id == udh.TranslationId)
                {
                    announce += "已被自动安保系统成功消灭。";
                }
                else
                {
                    announce += "已被成功消灭，死亡原因不明。";
                }
            }
            else if (handler is AttackerDamageHandler adh)
            {
                Player attacker = Player.Get(adh.Attacker.Hub);
                if (adh is ScpDamageHandler || adh is Scp939DamageHandler || (attacker?.IsCustomCharacter() == true && attacker?.GetCharacterInstance() is IScpNumber))
                {
                    string number = adh is Scp939DamageHandler ? "939" : adh.Attacker.Role.ToString().Replace("Scp", "");
                    if (attacker.GetCharacterInstance() is IScpNumber t)
                        number = t.ScpNumber;
                    announce += $"已被 SCP-{number.Replace("0492", "049-2")} 消灭。";
                }
                else
                {
                    switch (PlayerRolesUtils.GetTeam(adh.Attacker.Role))
                    {
                        case Team.ClassD:
                            announce += "已被D级人员重新收容。";
                            break;

                        case Team.Scientists:
                            announce += "已被科学家重新收容。";
                            break;

                        case Team.ChaosInsurgency:
                            announce += "已被混沌分裂者重新收容。";
                            break;

                        case Team.FoundationForces:
                            announce += $"已被{adh.Attacker.UnitName}成功收容。";
                            break;
                    }
                }
            }
            else if (handler is WarheadDamageHandler)
            {
                announce += "已被Alpha核弹头成功消灭。";
            }
            else
            {
                announce += "已被成功消灭，死亡原因不明。";
            }
            return announce;
        }

        public static string GetCustomRoleName(this Player player)
        {
            var cc = player.GetCharacterInstance();
            return $"<color={cc.RoleColor.ColorToHex()}>{cc.Attribute.Name}</color>";
        }
        public static bool IsCustomCharacter(this Player player) => GetCharacterInstance(player) != null;

        public static bool IsScp(this ReferenceHub hub, bool includeZombie = false)
        {
            Player player = Player.Get(hub);
            return hub.IsSCP(includeZombie) || (player != null && player.IsCustomCharacter() && player.GetSide() == Side.Scp);
        }
    }

    public abstract class CustomCharacter
    {
        public CustomCharacter(int id)
        {
            Id = id;
        }

        public static CoroutineHandle AbilityCoroutine { get; set; }
        public static List<CustomCharacter> AllCharacters { get; set; } = new List<CustomCharacter>();
        public static CoroutineHandle Coroutine { get; set; }
        public static Dictionary<SpawnableTeamType, byte> Waves { get; private set; } = new Dictionary<SpawnableTeamType, byte>();
        public RoleInfoAttribute Attribute => this.GetType().GetCustomAttribute<RoleInfoAttribute>();
        public abstract Color32 RoleColor { get; }
        public Player Player => Player.Get(Id);
        public int Id { get; }
        public string UserId => Player.UserId;
        public Dictionary<string, object> CharacterData { get; set; }
        public abstract IEnumerable<Ability.Ability> AbilityRegister();

        public static void SetEnabled()
        {
            Page pg = new Page(MenuInterface.Root, "RoleInfo", "技能说明", (Player player) =>
            {
                if (!AbilityManager.Abilities.TryGetValue(player, out var ability))
                {
                    return "技能说明 -[无技能]-";
                }
                return $"技能说明 -[<color=yellow>{ability.Count}</color> 个技能]-";
            }, (Player player, out string response) =>
            {
                response = "";
                if (!AbilityManager.Abilities.ContainsKey(player))
                {
                    response = "你没有技能";
                    return false;
                }
                return true;
            }, null, false);
            pg.Children = (Player player) =>
            {
                if (!AbilityManager.Abilities.TryGetValue(player, out var abilities))
                {
                    return Array.Empty<MenuElement>();
                }
                List<MenuElement> list = ListPool<MenuElement>.Pool.Get();
                int count = 0;
                foreach (var ability in abilities)
                {
                    Text title = new Text(pg, $"{ability.Ability.AbilityId}_Title", () =>
                    $"[{ability.Ability.Text}] {(ability.Ability.Type != AbilityType.Attribute ? $"<color=#00FFD7>[{ability.Ability.Cooldown}s]</color>" : "")}：{ability.Ability.Description}", false);
                    list.Add(title);
                    count++;
                    if (count >= 4)
                    {
                        count = 0;
                        list.Add(new LineBreak(pg));
                    }
                }
                var result = list.ToArray();
                ListPool<MenuElement>.Pool.Return(list);
                return result;
            };
            MenuInterface.RegisteredElements.Add(pg);
            AbilityManager.Initialize();
            EventHandlers.RegisterEvents();
            foreach (var type in CustomCharacterPlugin.instance.Manager.AllTypes)
                Manager_ObjectLoaded(type.Key);
            CustomCharacterPlugin.instance.Manager.ObjectLoaded += Manager_ObjectLoaded;
            //CustomCharacterPlugin.instance.Manager.ObjectUnloaded += Manager_ObjectUnloaded;
            ServerConsole.FriendlyFire = true;
            AttackerDamageHandler._ffMultiplier = 1f;
            VoiceChatManager.Init();
            ServerConfigSynchronizer.RefreshAllConfigs();
        }

        private static void Manager_ObjectUnloaded(Type type)
        {
        }

        private static void Manager_ObjectLoaded(Type type)
        {
            CustomCharacter instance = (CustomCharacter)Activator.CreateInstance(type, new object[] { (int)-1 });
            foreach (var ab in instance.AbilityRegister())
            {
                ab.RegisterAbility();
            }
        }

        public static void SpawnPlayer<T>(Player player) where T : CustomCharacter
        {
            SpawnPlayer(player, typeof(T));
        }
        public static void SpawnPlayer(Player player, Type roletype)
        {
            SpawnPlayer(player, roletype, RoleSpawnFlags.All);
        }
        
        public static void SpawnPlayer(Player player, Type roleType,RoleSpawnFlags roleSpawnFlags = RoleSpawnFlags.All)
        {
            try
            {
                Log.Info("开始生成：" + roleType.Name);
                if (!CustomCharacterPlugin.instance.Manager.AllTypes.ContainsKey(roleType) || player.IsCustomCharacter())
                {
                    return;
                }
                CustomCharacter cc = (CustomCharacter)Activator.CreateInstance(roleType, player.Id);
                AllCharacters.Add(cc);
                if (cc.Attribute.OriginRole != cc.Attribute.TargetRole && player.Role.Type != cc.Attribute.TargetRole && cc.Attribute.TargetRole != RoleTypeId.None)
                    player.Role.Set(cc.Attribute.TargetRole, Exiled.API.Enums.SpawnReason.ForceClass,roleSpawnFlags);
                Timing.CallDelayed(1.0f, delegate ()
                {
                    if (cc.Attribute.MaxHealth > 0)
                    {
                        player.MaxHealth = cc.Attribute.MaxHealth;
                        player.Health = player.MaxHealth;
                    }
                });
                Timing.CallDelayed(0.5f, delegate ()
                {
                    if (cc is ICustomSpawnpoint ics)
                    {
                        player.Teleport(ics.SpawnPosition);
                    }
                    if (cc is ICustomRotation rotation)
                    {
                        Timing.CallDelayed(0.1f, () => { player.Rotation = rotation.DefaultRotation; });
                    }
                    if (cc is IVoiceChannelModifcation mod)
                    {
                        foreach (var channel in mod.AvailableVoiceChannels)
                        {
                            player.VC_AddListener(channel);
                        }
                    }
                });
                cc.Role_Activate();
                var side = player.GetSide();
                string[] str = new string[]
                {
                "<size=65%><color=#BEBEBE>你成为了</color></color></size>",
                "",
                $"<size=150%><color={cc.RoleColor.ColorToHex()}>{cc.Attribute.Name}</color></size>",
                $"阵营：{(cc != null && cc.Attribute.FollowRole && !SideManager.CustomedSide.ContainsKey(player) ?  "<color=#FF00FF>跟随角色</color>" : side.FormattedName)}"
                };
                UpdateCustomInfo(cc);
                player.SendRawMsg("plugin_character", str, 10, ScreenLocation.CenterBottom, true);
            }
            catch (Exception e)
            {
                Log.Error(e);
            }
        }

        public static IEnumerator<float> Update()
        {
            while (true)
            {
                foreach (var player in Player.List)
                {
                    try
                    {
                        var sender = player.VC_GetSender();
                        var listener = player.VC_GetListener();
                        var cc = player.GetCharacterInstance();
                        bool flag2 = (cc != null && cc is IVoiceChannelModifcation mod && mod.AvailableVoiceChannels.Length > 0) || VoiceChatManager.PrimaryChannelModifier.ContainsKey(player) || listener.Length > 0 || sender.Length > 0 || SideManager.CustomedSide.ContainsKey(player) || player.Role.Type == RoleTypeId.Scp939;
                        if (AbilityManager.Abilities.ContainsKey(player) || flag2)
                        {
                            if (!player.HasMsg("ability"))
                                player.SendRawMsg("ability", HudElement, 0, ScreenLocation.Bottom, true);
                        }
                    }
                    catch (Exception e)
                    {
                        Log.Error(e);
                        continue;
                    }
                }
                yield return Timing.WaitForSeconds(1f);
            }
        }

        public static void UpdateCustomInfo(CustomCharacter cc)
        {
            Player player = Player.Get(cc.UserId);
            CustomSide side = player.GetSide();
            string text = $"<color={side.SideColor}>{cc.Attribute.Name}</color>";
            if (cc is IBadgeVisibilityModified mod)
            {
                player.CustomInfo = " ";
                player.InfoArea = PlayerInfoArea.Role | PlayerInfoArea.Badge | PlayerInfoArea.Nickname;
                foreach (Player teammate in Player.List)
                {
                    bool result = mod.SideIdList.Contains(teammate.GetSide().Id);
                    result = result != mod.IsBlacklist;
                    teammate.SendFakeSyncVar(player.NetworkIdentity, typeof(NicknameSync), nameof(NicknameSync.Network_customPlayerInfoString), result ? text : " ");
                    teammate.SendFakeSyncVar(player.NetworkIdentity, typeof(NicknameSync), nameof(NicknameSync.Network_playerInfoToShow),
                        result ? (int)(PlayerInfoArea.CustomInfo | PlayerInfoArea.Badge | PlayerInfoArea.Nickname) : (int)(PlayerInfoArea.Role | PlayerInfoArea.Badge | PlayerInfoArea.Nickname));
                }
            }
            else
            {
                player.CustomInfo = text;
                player.InfoArea = PlayerInfoArea.CustomInfo | PlayerInfoArea.Badge | PlayerInfoArea.Nickname;
            }
        }

        public virtual void Role_Activate()
        {
            CharacterData = DictionaryPool<string, object>.Pool.Get();
            foreach (var ability in AbilityManager.RegisteredAbilities.Where(x => x.Belongings?.Contains(this.GetType()) == true))
            {
                Player.GrantAbility(ability);
            }
        }

        public virtual void Role_Deactive()
        {
            DictionaryPool<string, object>.Pool.Return(CharacterData);
            Player.RevokeAbility();
            if (this is IVoiceChannelModifcation mod)
            {
                Player.VC_ResetPrimaryChannel();
                foreach (var channel in mod.AvailableVoiceChannels)
                {
                    Player.VC_RemoveSender(channel);
                    Player.VC_RemoveListener(channel);
                }
            }
        }
        public static string[] SpectatorHud(Player player)
        {
            try
            {
                if (!(player.ReferenceHub.roleManager.CurrentRole is SpectatorRole role))
                {
                    player.DeleteMsg("spectator", false);
                    return new string[] { " " };
                }
                Player tar = Player.Get(role.SyncedSpectatedNetId);
                if (tar == null)
                {
                    player.DeleteMsg("spectator", false);
                    return new string[] { " " };
                }
                var data = GameplayData.GameDatas.FirstOrDefault(x => x.Player == tar);
                int level = 0;
                int mig = 0;
                int mvp = 0;
                if (data != null)
                {
                    level = data.Level;
                    mvp = data.Mvp;
                    mig = data.Transmigration;
                }
                List<string> texts = ListPool<string>.Pool.Get();
                var c = tar.GetCharacterInstance();
                bool character = c != null && (!(c is IBadgeVisibilityModified mod) || mod.IsBlacklist);
                string rolename = character ? tar.GetCustomRoleName() : $"<color={tar.Role.Color.ToHex()}>{tar.Role.Type.GetRoleName()}</color>";
                texts.Add( $"正在观看：{rolename} | " +
                    $"等级: {GameplayData.GetLevelText(mig, level, true)}");
                if (!string.IsNullOrEmpty(tar.RankName))
                {
                    var color = ReferenceHub.LocalHub.serverRoles.NamedColors.FirstOrDefault(x => x.Name == tar.RankColor);
                    if (color != null) {
                        texts[texts.Count - 1] += $" | 称号：<color=#{color.ColorHex}>{tar.RankName}</color>";
                    }
                }
                if (character && AbilityManager.Abilities.TryGetValue(tar, out var abilities))
                {
                    string[] icon = new string[] { "❶", "❷", "❸", "❹", "❺", "❻", "❼", "❽", "❾", "❿" };
                    int i = 0;
                    foreach (var pro in abilities.Where(x => x.Ability.Type == AbilityType.Offensive))
                    {
                        if (i >= 3)
                        {
                            break;
                        }
                        texts.Add($"<size=40%><color=yellow>{icon[i]}</color>: {pro.CooldownStatus.GetText(tar, true)} [{pro.Ability.Description}]</size>");
                        i++;
                    }
                    var passes = abilities.Where(x => x.Ability.Type == AbilityType.Passive);
                    i = 0;
                    foreach (var pass in passes)
                    {
                        texts.Add($"<size=40%><color=#05A3AD>{(passes.Count() == 1 ? "被" : icon[i])}</color>: {pass.CooldownStatus.GetText(tar, true)} [{pass.Ability.Description}]</size>");
                        i++;
                    }
                }
                return ListPool<string>.Pool.ToArrayReturn(texts);
            }
            catch (Exception ex)
            {
                Log.Error(ex);
            }
            return new string[] { " " };
        }
        private static string[] HudElement(Player player)
        {
            try
            {
                CustomCharacter cc = player.GetCharacterInstance();
                var listener = player.VC_GetListener();
                var custom_listener = player.VC_GetCustomListener();
                var primary = player.GetDefaultCorrectChannel();
                var sender = player.VC_GetSender();
                var custom_sender = player.VC_GetCustomSender();
                bool flag1 = AbilityManager.Abilities.TryGetValue(player, out var abilities) || cc != null || SideManager.CustomedSide.ContainsKey(player);
                bool flag2 = (cc is IVoiceChannelModifcation mods && mods.AvailableVoiceChannels.Length > 0) || VoiceChatManager.PrimaryChannelModifier.ContainsKey(player) || listener.Length > 0 || sender.Length > 0 || custom_listener.Length >0 || custom_sender.Length > 0 || player.Role.Type == RoleTypeId.Scp939;
                bool flag3 = false;
                if (!flag1 && !flag2)
                {
                    player.DeleteMsg("ability", false);
                    return new string[] { "" };
                }
                var side = player.GetSide();
                string text1 = "";
                string text2 = "";
                if (flag1)
                {
                    string rolename = cc != null ? player.GetCustomRoleName() : $"<color={player.Role.Color.ToHex()}>{player.Role.Type.GetRoleName()}</color>";
                    text1 += $"正在扮演: {rolename} [{(cc != null && cc.Attribute.FollowRole && !SideManager.CustomedSide.ContainsKey(player) ?  "<color=#FF00FF>跟随角色</color>" : side.FormattedName)}]";
                    if (abilities != null)
                    {
                        string[] icon = new string[] { "❶", "❷", "❸", "❹", "❺", "❻", "❼", "❽", "❾", "❿" };
                        int i = 0;
                        foreach (var pro in abilities.Where(x => x.Ability.Type == AbilityType.Offensive))
                        {
                            if (i >= 3)
                            {
                                break;
                            }
                            text1 += $" | <color=yellow>{icon[i]}</color>: {pro.CooldownStatus.GetText(player, true)}";
                            i++;
                        }
                        var passes = abilities.Where(x => x.Ability.Type == AbilityType.Passive);
                        i = 0;
                        foreach (var pass in passes)
                        {
                            text1 += $" | <color=#05A3AD>{(passes.Count() == 1 ? "被" : icon[i])}</color>: {pass.CooldownStatus.GetText(player, true)}";
                            i++;
                        }
                        if (AbilityManager.AbilityInvalid.TryGetValue(player, out var time))
                        {
                            text1 += $" | <color=#AA0000>[技能失效: {(time - DateTime.Now).Seconds}s]</color>";
                        }
                    }
                    if (cc != null)
                    {
                        foreach (var prop in cc.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public)
                            .Where(x => x.GetCustomAttribute<DescriptionAttribute>() != null))
                        {
                            var key = prop.GetCustomAttribute<DescriptionAttribute>().Description;
                            if (flag3)
                                text2 += " | ";
                            text2 += $"{key}: <color=yellow>{prop.GetValue(cc)}</color>";
                            flag3 = true;
                        }
                    }
                }
                if (flag2)
                {
                    bool can_modify = cc != null && cc is IVoiceChannelModifcation mod && mod.AvailableVoiceChannels.Length > 0 || player.Role.Type == RoleTypeId.Scp939;
                    text2 += (flag3 ? " | " : "") + $"语音{(can_modify ? " <color=#00FF00>[Alt]</color>" : "")}：";
                    if (VoiceChatManager.PrimaryChannelModifier.ContainsKey(player))
                    {
                        primary = VoiceChatManager.PrimaryChannelModifier[player];
                    }
                        var xx1 = primary.GetChannel(out string hex1);
                        text2 += $" <color={hex1}>[{xx1}{(listener.Contains(primary) ? "" : "🎤")}]</color>";
                    foreach (var item in sender)
                    {
                        var xx = item.GetChannel(out string hex);
                        text2 += $" <color={hex}>[{xx}{(listener.Contains(primary) ? "" : "🎤")}]</color>";
                    }
                    foreach (var item in listener.Where(x => !sender.Contains(x) && primary != x))
                    {
                        var xx = item.GetChannel(out string hex);
                        text2 += $" <color={hex}>[{xx}🎧]</color>";
                    }
                    foreach (var item in VoiceChatManager.CustomChannels)
                    {
                        bool send = item.Sender.Contains(player);
                        bool listen = item.Listener.Contains(player);
                        if (send || listen)
                        {
                            var txt = $" <color={item.ColorHex}>[{item.Name}%ICON%]</color>";
                            if (send && listen)
                                txt = txt.Replace("%ICON%", "");
                            else
                                txt = txt.Replace("%ICON%", send ? "🎤" : "🎧");
                            text2 += txt;
                        }
                    }
                }
                string raw = text1 + text2;
                int index = raw.IndexOf('<');
                while (index != -1)
                {
                    int suffix = raw.IndexOf('>');
                    if (suffix == -1)
                        break;
                    raw = raw.Remove(index, suffix - index + 1);
                    index = raw.IndexOf('<');
                }
                if (Encoding.Default.GetByteCount(raw) >= 150)
                    return new string[] { text1, text2 };
                else
                    return new string[] { $"{text1} | {text2}" };
            }
            catch (Exception e)
            { Log.Error(e); }
            return new string[] { "" };
        }
    }

    public class CustomRoleReservation
    {
        public CustomRoleReservation(string userid, Type roletype)
        {
            UserId = userid;
            RoleType = roletype;
            var attr = roletype.GetCustomAttribute<RoleInfoAttribute>();
            var res = new RoleReservation(userid, attr.OriginRole);
            Reservation = res;
            RoleReservation.Reservations.Add(res);
            res.OnFailed += Failed;
        }

        public event Action<string> OnFailed;

        public event Action<string> OnSuccess;

        public static List<CustomRoleReservation> Reservations { get; private set; } = new List<CustomRoleReservation>();
        public string Inventory_Item_Name { get; private set; }
        public RoleReservation Reservation { get; private set; }
        public Type RoleType { get; }
        public string UserId { get; }

        public void Failed(string userid)
        {
            Reservations.Remove(this);
            OnFailed?.Invoke(UserId);
        }

        public void Success(string userid)
        {
            Reservations.Remove(this);
            OnSuccess?.Invoke(UserId);
        }
    }

    [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
    public class RoleInfoAttribute : Attribute, IName
    {
        public RoleInfoAttribute(string name, Side side, RoleTypeId original, RoleTypeId target) : this(name, side.ToString(), original, target)
        {
        }
        public RoleInfoAttribute(string name, string side, RoleTypeId original, RoleTypeId target)
        {
            FollowRole = string.IsNullOrEmpty(side);
            Name = name;
            TargetRole = target;
            OriginRole = original;
            SideId = side;
        }
        public RoleInfoAttribute(string name, RoleTypeId original, RoleTypeId target) : this(name, "", original, target)
        {
        }

        /// <summary>
        /// 可用的游戏模式， 一定会包括默认模式
        /// </summary>
        public Type[] AvailableGameMode { get; set; } = Array.Empty<Type>();

        public int MaxHealth { get; set; } = 0;
        public string Name { get; }
        public RoleTypeId OriginRole { get; }
        public string SideId { get; }
        public RoleTypeId TargetRole { get; }
        public bool FollowRole { get; }
    }
}